<template>
  <div
    ref="flexibleLayout"
    :class="{
      'outer-container': true,
      'row-container': layoutDirection == 'horizontal',
      'column-container': layoutDirection == 'vertical',
    }"
  >
    <template v-for="(component, index) in components" :key="component.name">
      <component
        :id="component.name"
        :ref="setItemRef"
        :is="statusCenter.getRegisteredComponent(component.is)"
        :class="{
          'outer-item': true,
          'highlight-right': highlightBorder(component.name) == 'right',
          'highlight-left': highlightBorder(component.name) == 'left',
          'highlight-top': highlightBorder(component.name) == 'top',
          'highlight-bottom': highlightBorder(component.name) == 'bottom',
        }"
        :style="
          layoutDirection == 'horizontal'
            ? { width: component.mainSize + 'px' }
            : { height: component.mainSize + 'px' }
        "
        :name="component.name"
        :item-uuid="component.uuid"
        v-bind="component.params"
        @drop.stop="drop(component.name, $event)"
        @dragover.stop="allowDrop(component.name, $event)"
        @dragleave="onDragLeave(component.name)"
        @detach="onItemDetach(component.name)"
        @close="onItemClose(component.name)"
      >
      </component>
      <div
        :class="{
          'vertical-margin': layoutDirection == 'horizontal',
          'horizontal-margin': layoutDirection == 'vertical',
        }"
        @mousedown="draggingMargin = index"
        v-if="index < components.length - 1"
      ></div>
    </template>
  </div>
</template>

<script setup lang="ts">
import {
  onMounted,
  ref,
  defineProps,
  PropType,
  defineAsyncComponent,
  onBeforeMount,
  computed,
  nextTick,
  watch,
  onBeforeUpdate,
  defineExpose,
} from "vue";
import { arrInsert, sumProperty } from "../utils/utils";
import {
  emitRemoveComponent,
  setHandleRemoveComponent,
  emitRevalidateTeleportTarget,
  setOnReshowRemovedComponent,
} from "./status";
import { nanoid } from "nanoid";
import { addObserver } from "./observer";
import { store } from "./store";
import { statusCenter } from "../scripts/status";
import { LayoutItemComponent } from "@/scripts/models";

/**
 * 布局的属性。
 *
 */
const props = defineProps({
  layoutDirection: {
    // 布局方向，分为垂直和水平两种。
    type: String as PropType<"vertical" | "horizontal">,
    required: true,
  },
  components: {
    // 其上放置的组件类型。
    type: Object as PropType<LayoutItemComponent[]>,
    required: true,
  },
  name: {
    // 布局的名称。如果创建元素，其在DOM的id与name相同。
    type: String,
    required: true,
  },
});

const flexibleLayout = ref(null);

const components = ref<LayoutItemComponent[]>([]);

// 当前正在被拖拽的分割线索引
const draggingMargin = ref(-1);

const currentDrop = ref({
  target: "",
  direction: "" as "prev" | "next",
});

let itemRefs: any[] = [];
const setItemRef = (el: HTMLDivElement) => {
  if (el) {
    itemRefs.push(el);
  }
};
onBeforeUpdate(() => {
  itemRefs = [];
});

const onDragLeave = (component: string) => {
  console.log("drag leave!", component);
  currentDrop.value.target = "";
};

const drop = async (targetName: string, evt: DragEvent) => {
  const name = evt.dataTransfer?.getData("layout-item-name");
  const containerUUID = evt.dataTransfer?.getData("layout-item-uuid");
  const target = getComponentByName(targetName);

  if (name != null && containerUUID != null) {
    // 如果目标名称与原先容器名称相同，相当于不移动，因此直接移除。
    if (name === targetName) {
      return;
    }
    const component = JSON.parse(
      JSON.stringify(statusCenter.getLayoutComponent(name))
    );
    if (component == null) {
      console.error("error occurred, name: ", name, statusCenter);
      throw Error;
    }
    const mainPos =
      props.layoutDirection == "vertical" ? evt.offsetY : evt.offsetX;

    let insertIndex = target.index;
    if (mainPos > target.component.mainSize / 2) {
      insertIndex += 1;
    }
    if (component == null) {
      throw Error("component was null.");
    }
    component.uuid = nanoid();
    statusCenter.setLayoutComponent(name, component);
    components.value = components.value
      .slice(0, insertIndex)
      .concat(
        [component].concat(
          components.value.slice(insertIndex, components.value.length)
        )
      );
    currentDrop.value.target = "";
    await nextTick();

    emitRemoveComponent(containerUUID);
    await nextTick();
    emitRevalidateTeleportTarget(name);
  }
};

const getComponentByName = (
  name: string
): { component: LayoutItemComponent; index: number } => {
  const targetComponentIndex = components.value.findIndex(
    (component) => component.name == name
  );
  if (targetComponentIndex == -1) {
    console.error(name, components);
    throw Error;
  }
  return {
    component: components.value[targetComponentIndex],
    index: targetComponentIndex,
  };
};

const allowDrop = (name: string, evt: DragEvent) => {
  const mainPos =
    props.layoutDirection == "vertical" ? evt.offsetY : evt.offsetX;
  // let insertIndex = index;
  currentDrop.value.target = name;
  const { component } = getComponentByName(name);
  if (mainPos > component.mainSize / 2) {
    currentDrop.value.direction = "next";
  } else {
    currentDrop.value.direction = "prev";
  }

  console.log(evt.offsetX, evt.offsetY);

  evt.preventDefault();
};

const onMouseDown = () => {
  console.log("mouse-down!");
};
const recomputeComponentSize = () => {
  // console.log(props.name, "resized");
  const layout = flexibleLayout.value as HTMLDivElement | null;
  if (layout == null) {
    return;
  }
  const containerMainSize =
    props.layoutDirection == "horizontal"
      ? layout.clientWidth
      : layout.clientHeight;
  let sizeSum = 0;
  for (const component of components.value) {
    sizeSum += component.mainSize;
  }
  for (const component of components.value) {
    component.mainSize = Math.round(
      (containerMainSize / sizeSum) * component.mainSize
    );
  }
  getSubLayouts();
};

const getSubLayouts = () => {
  for (const item of itemRefs) {
    if (item.recomputeComponentSize != null) {
      item.recomputeComponentSize();
    }
  }
};

const getScalableComponents = (marginIndex: number, dir: "prev" | "next") => {
  const scalableComponents = [];
  components.value.forEach((component) => {
    if (component.minSize) {
      scalableComponents.push();
    }
  });
};
watch(
  props.components,
  (newval, oldval) => {
    console.log("组件发生变化", props.name, oldval, newval);
  },
  { deep: false }
);

// 组件是否允许缩小尺寸
const componentShrinkable = (componentIndex: number) => {
  return (
    components.value[componentIndex].mainSize >
    components.value[componentIndex].minSize
  );
};

// 获取允许缩小尺寸的组件
const getShrinkableComponent = (marginIndex: number, dir: "prev" | "next") => {
  if (dir == "prev") {
    for (let i = marginIndex; i >= 0; i--) {
      if (componentShrinkable(i)) {
        return components.value[i];
      }
    }
  } else {
    for (let i = marginIndex + 1; i < components.value.length; i++) {
      if (componentShrinkable(i)) {
        return components.value[i];
      }
    }
  }
};

// 调整组件的尺寸。
const resizeComponent = (marginIndex: number, movement: number) => {
  const previousComponent =
    movement > 0
      ? components.value[marginIndex]
      : getShrinkableComponent(marginIndex, "prev");
  const nextComponent =
    movement > 0
      ? getShrinkableComponent(marginIndex, "next")
      : components.value[marginIndex + 1];
  if (nextComponent == null || previousComponent == null) {
    return;
  }
  if (
    previousComponent.mainSize + movement >= previousComponent.minSize &&
    nextComponent.mainSize - movement >= nextComponent.minSize
  ) {
    previousComponent.mainSize += movement;
    nextComponent.mainSize -= movement;
  } else {
    const movementWithConstraint =
      (movement > 0
        ? nextComponent.mainSize - nextComponent.minSize
        : previousComponent.mainSize - previousComponent.minSize) *
      (movement / Math.abs(movement));
    previousComponent.mainSize += movementWithConstraint;
    nextComponent.mainSize -= movementWithConstraint;
  }
};

onBeforeMount(() => {
  components.value = props.components;
  for (const component of components.value) {
    statusCenter.setLayoutComponent(component.name, component);
  }
});

onMounted(() => {
  recomputeComponentSize();
  window.addEventListener("mousemove", (evt: MouseEvent) => {
    if (evt.buttons == 1 && draggingMargin.value >= 0) {
      const movement =
        props.layoutDirection == "horizontal" ? evt.movementX : evt.movementY;
      resizeComponent(draggingMargin.value, movement);
    }
  });
  window.addEventListener("mouseup", (evt: MouseEvent) => {
    if (draggingMargin.value >= 0) {
      draggingMargin.value = -1;
    }
  });
  const targetNode = flexibleLayout.value;
  if (targetNode == null) {
    console.error("No target node defined");
    return;
  } else {
    addObserver(targetNode, () => {
      recomputeComponentSize();
    });
  }
});

setHandleRemoveComponent((uuid: string) => {
  const index = components.value.findIndex(
    (component) => component.uuid == uuid
  );
  if (index != -1) {
    components.value.splice(index, 1);
  } else {
    console.error("Cannot remove the sub component with uuid", uuid);
  }
});

const highlightBorder = computed(() => {
  return (componentName: string): "left" | "right" | "bottom" | "top" | "" => {
    if (currentDrop.value.target == componentName) {
      if (currentDrop.value.direction == "prev") {
        return props.layoutDirection == "vertical" ? "top" : "left";
      } else {
        return props.layoutDirection == "vertical" ? "bottom" : "right";
      }
    }
    return "";
  };
});

// 当其中一个子组件关闭时，将获取该组件的父组件的名称。
//
const onItemClose = (itemName: string) => {
  const index = components.value.findIndex((c) => c.name == itemName);
  if (index != -1) {
    const component = components.value[index];
    component.posMeta = {
      type: "layout",
      parent: props.name,
      // 获取前一个组件。如果前一个组件为空，则为空。
      lastComponent: index == 0 ? "" : components.value[index - 1].name,
    };
    components.value.splice(index, 1);
  }
};

setOnReshowRemovedComponent(async (targetID: string) => {
  const component = statusCenter.getLayoutComponent(targetID);
  if (
    component != null &&
    component.posMeta != null &&
    component.posMeta.parent == props.name
  ) {
    store.state.hiddenTargets.delete(targetID);
    const index = components.value.findIndex(
      (c) => c.name == component.posMeta?.lastComponent
    );
    if (index == -1) {
      components.value = [component].concat(components.value);
    } else {
      components.value = arrInsert(components.value, component, index + 1);
    }
    await nextTick();
    emitRevalidateTeleportTarget(targetID);
  }
});

// 处理各项内容detach的事件
const onItemDetach = (itemID: string) => {
  console.log("detach", itemID);
};
const dumpLayout = (layout: LayoutItemComponent[]): LayoutItemComponent[] => {
  let dumped: LayoutItemComponent[] = [];
  for (const index in itemRefs) {
    const item = itemRefs[index];
    if (item.dumpLayout != null) {
      const layoutComponent = components.value[index];
      layoutComponent.params!.components = item.dumpLayout();
      dumped.push(layoutComponent);
    } else {
      dumped.push(components.value[index]);
    }
  }
  return dumped;
};

defineExpose({ recomputeComponentSize, dumpLayout });
</script>

<style scoped>
.outer-container {
  display: flex;
  flex-direction: row;
  width: 100%;
}

.row-container {
  flex-direction: row;
}
.column-container {
  flex-direction: column;
}

.vertical-margin {
  background-color: aliceblue;
  width: 10px;
  height: 100%;
}

.horizontal-margin {
  background-color: aliceblue;
  height: 10px;
  width: 100%;
}

.highlight-right {
  border-right: 10px solid red;
}

.highlight-left {
  border-left: 10px solid red;
}

.highlight-top {
  border-top: 10px solid red;
}
.highlight-bottom {
  border-bottom: 10px solid red;
}

.left {
  height: 100%;
}

.main {
  height: 100%;
}

.side {
  height: 100%;
}
</style>
